﻿using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Schema;

namespace TemplateMatch
{
    public partial class frmTemplateMatch : Form
    {
        private Mat TargetImage;
        private Mat ImageToSearch;
        private Mat ImageToDisplay;
        private List<Template> Templates;
        private string TemplatePath;
        private MatchConfig config;
        public frmTemplateMatch()
        {
            InitializeComponent();
        }

        private void matchToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void frmTemplateMatch_Load(object sender, EventArgs e)
        {
            imageBox1.Text = "No drawing loaded!";
            menuMatch.Enabled = false;
            TemplatePath = @"D:\ComputerVision\OpenCV\Coding\VS\TemplateMatch\Data\Templates\2020-07-24";

            config = new MatchConfig();
        }

        private void menuLoadTarget_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Select drawing to search...";
            openFileDialog1.Filter = "Drawing|*.jpg;*.png";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                TargetImage = Cv2.ImRead(openFileDialog1.FileName);
                ImageToDisplay = Cv2.ImRead(openFileDialog1.FileName);
                ImageToSearch = new Mat();
                ImageToDisplay.CopyTo(ImageToSearch);
                imageBox1.Image = ImageToDisplay.ToBitmap();
                imageBox1.Text = Path.GetFileNameWithoutExtension(openFileDialog1.FileName);
            }

            if(listView1.Items.Count > 0)
            {
                menuMatch.Enabled = true;
            }
            else
            {
                menuMatch.Enabled = false;
            }
        }

        private void menuLoadTemplates_Click(object sender, EventArgs e)
        {
            folderBrowserDialog1.SelectedPath = TemplatePath;
            if(folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                TemplatePath = folderBrowserDialog1.SelectedPath;
                Templates = new List<Template>();
                int index = 0;
                imageList1.ImageSize = new System.Drawing.Size(48, 32);
                imageList1.Images.Clear();
                foreach(string f in Directory.GetFiles(TemplatePath))
                {
                    string name = Path.GetFileNameWithoutExtension(f);
                    string ext = Path.GetExtension(f);
                    if (ext.ToUpper() == ".JPG" || ext.ToUpper() == ".PNG")
                    {
                        Image img = Image.FromFile(f);
                        imageList1.Images.Add(name, img);
                        Template t = new Template(index);
                        t.Name = name;
                        t.SetImage(img);
                        Templates.Add(t);
                    }
                }

                listView1.LargeImageList = imageList1;
                listView1.Items.Clear();
                for (int i = 0; i < imageList1.Images.Count; i++)
                {
                    ListViewItem item = new ListViewItem();
                    item.ImageIndex = i;
                    item.Text = imageList1.Images.Keys[i];
                    listView1.Items.Add(item);
                }

                if (imageBox1.Image != null)
                {
                    menuMatch.Enabled = true;
                }
            }
        }

        private List<Rect> MatchTemplate(Image TargetImage, Image TemplateImage, double scaleStart, double scaleEnd, double scaleStep)
        {
            List<Rect> BoundingBoxes = new List<Rect>();

            //Convert image to Mat
            Mat target = BitmapConverter.ToMat(new Bitmap(TargetImage));
            Mat template = BitmapConverter.ToMat(new Bitmap(TemplateImage));
            //Convert to gray
            Cv2.CvtColor(target, target, ColorConversionCodes.BGR2GRAY);
            Cv2.CvtColor(template, template, ColorConversionCodes.BGR2GRAY);
            //convert to black and white
            Cv2.Threshold(target, target, 127, 255, ThresholdTypes.BinaryInv);
            Cv2.Threshold(template, template, 127, 255, ThresholdTypes.BinaryInv);

            //scale the template
            int width = template.Width;
            int height = template.Height;
            int w, h, bestW = width, bestH = height;
            int steps = Convert.ToInt32((scaleEnd - scaleStart) / scaleStep);
            double bestValue = 0;
            
            progressbar1.Visible = true;
            progressbar1.Minimum = 1;
            progressbar1.Maximum = steps;

            for (int i = 0; i < steps; i++)
            {
                double curScale = scaleStart + scaleStart * i;
                w = Convert.ToInt32(width * curScale);
                h = Convert.ToInt32(height * curScale);
                Mat curTemplate = new Mat();
                Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                //match scaled template step by step
                Mat result = new Mat();
                Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                double minValue, maxValue;
                OpenCvSharp.Point minLoc, maxLoc;
                Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                if (bestValue < maxValue)
                {
                    bestValue = maxValue;
                    bestW = w;
                    bestH = h;
                }
                progressbar1.Value = i + 1;
                System.Windows.Forms.Application.DoEvents();
                result.Dispose();
                curTemplate.Dispose();
            }

            //match again using the best scale
            Mat bestTemplate = new Mat();
            Mat bestResult = new Mat();
            Cv2.Resize(template, bestTemplate, new OpenCvSharp.Size(bestW, bestH));
            Cv2.MatchTemplate(target, bestTemplate, bestResult, TemplateMatchModes.CCoeffNormed);
            float thresh = 0.6f;
            List<MatchPoint> points = new List<MatchPoint>();
            for (int i = 0; i < bestResult.Rows; i++)
                for (int j = 0; j < bestResult.Cols; j ++)
                {
                    float value = bestResult.Get<float>(i, j);
                    if(value >= thresh)
                    {
                        ////注意，rectagnle起始点的x对应Mat的j，y对应Mat的i
                        //Rect box = new Rect(j, i, bestW, bestH);
                        //BoundingBoxes.Add(box);
                        MatchPoint p = new MatchPoint();
                        p.matchValue = value;
                        p.TopLeftPoint = new OpenCvSharp.Point(j, i);
                        p.Status = 0;
                        points.Add(p);
                    }
                }

            bestTemplate.Dispose();
            bestResult.Dispose();

            //remove overlapped bounding boxes
            //sort points by match value
            Comparison<MatchPoint> c = new Comparison<MatchPoint>
                (
                    (MatchPoint a, MatchPoint b) =>
                    {
                        if (a.matchValue < b.matchValue)
                            return 1;
                        else if (a.matchValue == b.matchValue)
                            return 0;
                        else
                            return -1;
                    }
                );
            points.Sort(c);
            //get the point with highest match value, then identify if any other points are too close to this point and remove them
            for (int i = 0; i < points.Count; i++)
            {
                if (points[i].Status == 0)
                {
                    for (int j = i + 1; j < points.Count; j++)
                    {
                        int dx = Math.Abs(points[i].TopLeftPoint.X - points[j].TopLeftPoint.X);
                        int dy = Math.Abs(points[i].TopLeftPoint.Y - points[j].TopLeftPoint.Y);
                        if ((dx < bestW / 2) && (dy < bestH / 2))
                        {
                            points[j].Status = 1;
                        }
                    }
                }
            }

            foreach (MatchPoint mp in points)
            {
                if (mp.Status == 0)
                {
                    Rect box = new Rect(mp.TopLeftPoint, new OpenCvSharp.Size(bestW, bestH));
                    BoundingBoxes.Add(box);
                }
            }

            return BoundingBoxes;
        }

        private List<Rect> MatchTemplate(Image TargetImage, Image TemplateImage)
        {
            List<Rect> BoundingBoxes = new List<Rect>();

            //Convert image to Mat
            Mat target = BitmapConverter.ToMat(new Bitmap(TargetImage));
            Mat template = BitmapConverter.ToMat(new Bitmap(TemplateImage));
            //Convert to gray
            Cv2.CvtColor(target, target, ColorConversionCodes.BGR2GRAY);
            Cv2.CvtColor(template, template, ColorConversionCodes.BGR2GRAY);
            //convert to black and white
            Cv2.Threshold(target, target, 127, 255, ThresholdTypes.BinaryInv);
            Cv2.Threshold(template, template, 127, 255, ThresholdTypes.BinaryInv);

            //scale the template
            int width = template.Width;
            int height = template.Height;
            int w, h, bestW = width, bestH = height;
            int xSteps = 0;
            int ySteps = 0;
            if (config.xScaleFrom == config.xScaleTo)
            {
                xSteps = 1;
            }
            else
            {
                xSteps = Convert.ToInt32((config.xScaleTo - config.xScaleFrom) / config.xStep);
            }
            if (config.yScaleFrom == config.yScaleTo)
            {
                ySteps = 1;
            }
            else
            {
                ySteps = Convert.ToInt32((config.yScaleTo - config.yScaleFrom) / config.yStep);
            }            
            
            double bestValue = 0;

            progressbar1.Visible = true;
            progressbar1.Minimum = 1;

            if (config.Reform)
            {
                progressbar1.Maximum = xSteps * ySteps;
                for (int i = 0; i < xSteps; i++)
                {
                    double xScale = config.xScaleFrom + config.xStep * i;
                    w = Convert.ToInt32(width * xScale);
                    for(int j = 0; j < ySteps; j++)
                    {
                        double yScale = config.yScaleFrom + config.yStep * j;
                        h = Convert.ToInt32(height * yScale);
                        Mat curTemplate = new Mat();
                        Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                        //match scaled template step by step
                        Mat result = new Mat();
                        Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                        double minValue, maxValue;
                        OpenCvSharp.Point minLoc, maxLoc;
                        Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                        if (bestValue < maxValue)
                        {
                            bestValue = maxValue;
                            bestW = w;
                            bestH = h;
                        }
                        progressbar1.Value = i + 1;
                        System.Windows.Forms.Application.DoEvents();
                        result.Dispose();
                        curTemplate.Dispose();
                    }
                }
            }
            else
            {
                progressbar1.Maximum = xSteps;
                for (int i = 0; i < xSteps; i++)
                {
                    double curScale = config.xScaleFrom + config.xStep * i;
                    w = Convert.ToInt32(width * curScale);
                    h = Convert.ToInt32(height * curScale);
                    Mat curTemplate = new Mat();
                    Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                    //match scaled template step by step
                    Mat result = new Mat();
                    Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                    double minValue, maxValue;
                    OpenCvSharp.Point minLoc, maxLoc;
                    Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                    if (bestValue < maxValue)
                    {
                        bestValue = maxValue;
                        bestW = w;
                        bestH = h;
                    }
                    progressbar1.Value = i + 1;
                    System.Windows.Forms.Application.DoEvents();
                    result.Dispose();
                    curTemplate.Dispose();
                }
            }


            //match again using the best scale
            Mat bestTemplate = new Mat();
            Mat bestResult = new Mat();
            Cv2.Resize(template, bestTemplate, new OpenCvSharp.Size(bestW, bestH));
            Cv2.MatchTemplate(target, bestTemplate, bestResult, TemplateMatchModes.CCoeffNormed);
            float thresh = 0.6f;
            List<MatchPoint> points = new List<MatchPoint>();
            for (int i = 0; i < bestResult.Rows; i++)
                for (int j = 0; j < bestResult.Cols; j++)
                {
                    float value = bestResult.Get<float>(i, j);
                    if (value >= thresh)
                    {
                        ////注意，rectagnle起始点的x对应Mat的j，y对应Mat的i
                        //Rect box = new Rect(j, i, bestW, bestH);
                        //BoundingBoxes.Add(box);
                        MatchPoint p = new MatchPoint();
                        p.matchValue = value;
                        p.TopLeftPoint = new OpenCvSharp.Point(j, i);
                        p.Status = 0;
                        points.Add(p);
                    }
                }

            bestTemplate.Dispose();
            bestResult.Dispose();

            //remove overlapped bounding boxes
            //sort points by match value
            Comparison<MatchPoint> c = new Comparison<MatchPoint>
                (
                    (MatchPoint a, MatchPoint b) =>
                    {
                        if (a.matchValue < b.matchValue)
                            return 1;
                        else if (a.matchValue == b.matchValue)
                            return 0;
                        else
                            return -1;
                    }
                );
            points.Sort(c);
            //get the point with highest match value, then identify if any other points are too close to this point and remove them
            for (int i = 0; i < points.Count; i++)
            {
                if (points[i].Status == 0)
                {
                    for (int j = i + 1; j < points.Count; j++)
                    {
                        int dx = Math.Abs(points[i].TopLeftPoint.X - points[j].TopLeftPoint.X);
                        int dy = Math.Abs(points[i].TopLeftPoint.Y - points[j].TopLeftPoint.Y);
                        if ((dx < bestW / 2) && (dy < bestH / 2))
                        {
                            points[j].Status = 1;
                        }
                    }
                }
            }

            foreach (MatchPoint mp in points)
            {
                if (mp.Status == 0)
                {
                    Rect box = new Rect(mp.TopLeftPoint, new OpenCvSharp.Size(bestW, bestH));
                    BoundingBoxes.Add(box);
                }
            }

            return BoundingBoxes;
        }


        private List<Rect> MatchTemplateReform(Mat TargetImage, Mat TemplateImage, double scaleStart, double scaleEnd, double scaleStep, double thresh)
        {
            List<Rect> BoundingBoxes = new List<Rect>();

            //Convert image to Mat
            Mat target = new Mat();
            Mat template = new Mat();
            //Convert to gray
            Cv2.CvtColor(TargetImage, target, ColorConversionCodes.BGR2GRAY);
            Cv2.CvtColor(TemplateImage, template, ColorConversionCodes.BGR2GRAY);
            //convert to black and white
            Cv2.Threshold(target, target, 127, 255, ThresholdTypes.BinaryInv);
            Cv2.Threshold(template, template, 127, 255, ThresholdTypes.BinaryInv);
            //Cv2.GaussianBlur(target, target, new OpenCvSharp.Size(config.BlurKernelSize, config.BlurKernelSize), 1);
            

            //scale the template
            int width = template.Width;
            int height = template.Height;
            int w, h, bestW = width, bestH = height;
            int steps = Convert.ToInt32((scaleEnd - scaleStart) / scaleStep);
            double bestValue = 0;

            progressbar1.Visible = true;
            progressbar1.Minimum = 1;
            progressbar1.Maximum = steps;

            for (int i = 0; i < steps; i++)
            {
                double xScale = scaleStart + scaleStep * i;
                w = Convert.ToInt32(width * xScale);
                for (int j = 0; j < steps; j++)
                {
                    double yScale = scaleStart + scaleStep * j;
                    h = Convert.ToInt32(height * yScale);
                    Mat curTemplate = new Mat();
                    Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                    //match scaled template step by step
                    Mat result = new Mat();
                    Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                    double minValue, maxValue;
                    OpenCvSharp.Point minLoc, maxLoc;
                    Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                    if (bestValue < maxValue)
                    {
                        bestValue = maxValue;
                        bestW = w;
                        bestH = h;
                    }
                    progressbar1.Value = i + 1;
                    System.Windows.Forms.Application.DoEvents();
                    result.Dispose();
                    curTemplate.Dispose();
                }
            }
            //match again using the best scale
            Mat bestTemplate = new Mat();
            Mat bestResult = new Mat();
            Cv2.Resize(template, bestTemplate, new OpenCvSharp.Size(bestW, bestH));
            Cv2.MatchTemplate(target, bestTemplate, bestResult, TemplateMatchModes.CCoeffNormed);
            List<MatchPoint> points = new List<MatchPoint>();
            for (int i = 0; i < bestResult.Rows; i++)
                for (int j = 0; j < bestResult.Cols; j++)
                {
                    float value = bestResult.Get<float>(i, j);
                    if (value >= thresh)
                    {
                        ////注意，rectagnle起始点的x对应Mat的j，y对应Mat的i
                        //Rect box = new Rect(j, i, bestW, bestH);
                        //BoundingBoxes.Add(box);
                        MatchPoint p = new MatchPoint();
                        p.matchValue = value;
                        p.TopLeftPoint = new OpenCvSharp.Point(j, i);
                        p.Status = 0;
                        points.Add(p);
                    }
                }

            bestTemplate.Dispose();
            bestResult.Dispose();

            //remove overlapped bounding boxes
            //sort points by match value
            Comparison<MatchPoint> c = new Comparison<MatchPoint>
                (
                    (MatchPoint a, MatchPoint b) =>
                    {
                        if (a.matchValue < b.matchValue)
                            return 1;
                        else if (a.matchValue == b.matchValue)
                            return 0;
                        else
                            return -1;
                    }
                );
            points.Sort(c);
            //get the point with highest match value, then identify if any other points are too close to this point and remove them
            for (int i = 0; i < points.Count; i++)
            {
                if (points[i].Status == 0)
                {
                    for (int j = i + 1; j < points.Count; j++)
                    {
                        int dx = Math.Abs(points[i].TopLeftPoint.X - points[j].TopLeftPoint.X);
                        int dy = Math.Abs(points[i].TopLeftPoint.Y - points[j].TopLeftPoint.Y);
                        if ((dx < bestW / 2) && (dy < bestH / 2))
                        {
                            points[j].Status = 1;
                        }
                    }
                }
            }

            foreach (MatchPoint mp in points)
            {
                if (mp.Status == 0)
                {
                    Rect box = new Rect(mp.TopLeftPoint, new OpenCvSharp.Size(bestW, bestH));
                    BoundingBoxes.Add(box);
                }
            }

            return BoundingBoxes;
        }

        private List<Rect> MatchTemplateScale(Mat TargetImage, Mat TemplateImage, double scaleStart, double scaleEnd, double scaleStep, double thresh)
        {
            List<Rect> BoundingBoxes = new List<Rect>();

            //Convert image to Mat
            Mat target = new Mat();
            Mat template = new Mat();
            //Convert to gray
            Cv2.CvtColor(TargetImage, target, ColorConversionCodes.BGR2GRAY);
            Cv2.CvtColor(TemplateImage, template, ColorConversionCodes.BGR2GRAY);
            //convert to black and white
            Cv2.Threshold(target, target, 127, 255, ThresholdTypes.BinaryInv);
            Cv2.Threshold(template, template, 127, 255, ThresholdTypes.BinaryInv);
            //Cv2.GaussianBlur(target, target, new OpenCvSharp.Size(config.BlurKernelSize, config.BlurKernelSize), 1);


            //scale the template
            int width = template.Width;
            int height = template.Height;
            int w, h, bestW = width, bestH = height;
            int steps = Convert.ToInt32((scaleEnd - scaleStart) / scaleStep);
            double bestValue = 0;

            progressbar1.Visible = true;
            progressbar1.Minimum = 1;
            progressbar1.Maximum = steps;

            for (int i = 0; i < steps; i++)
            {
                double xScale = scaleStart + scaleStep * i;
                w = Convert.ToInt32(width * xScale);
                double yScale = scaleStart + scaleStep * i;
                h = Convert.ToInt32(height * yScale);
                Mat curTemplate = new Mat();
                Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                //match scaled template step by step
                Mat result = new Mat();
                Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                double minValue, maxValue;
                OpenCvSharp.Point minLoc, maxLoc;
                Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                if (bestValue < maxValue)
                {
                    bestValue = maxValue;
                    bestW = w;
                    bestH = h;
                }
                progressbar1.Value = i + 1;
                System.Windows.Forms.Application.DoEvents();
                result.Dispose();
                curTemplate.Dispose();
            }
            //match again using the best scale
            Mat bestTemplate = new Mat();
            Mat bestResult = new Mat();
            Cv2.Resize(template, bestTemplate, new OpenCvSharp.Size(bestW, bestH));
            Cv2.MatchTemplate(target, bestTemplate, bestResult, TemplateMatchModes.CCoeffNormed);
            List<MatchPoint> points = new List<MatchPoint>();
            for (int i = 0; i < bestResult.Rows; i++)
                for (int j = 0; j < bestResult.Cols; j++)
                {
                    float value = bestResult.Get<float>(i, j);
                    if (value >= thresh)
                    {
                        ////注意，rectagnle起始点的x对应Mat的j，y对应Mat的i
                        //Rect box = new Rect(j, i, bestW, bestH);
                        //BoundingBoxes.Add(box);
                        MatchPoint p = new MatchPoint();
                        p.matchValue = value;
                        p.TopLeftPoint = new OpenCvSharp.Point(j, i);
                        p.Status = 0;
                        points.Add(p);
                    }
                }

            bestTemplate.Dispose();
            bestResult.Dispose();

            //remove overlapped bounding boxes
            //sort points by match value
            Comparison<MatchPoint> c = new Comparison<MatchPoint>
                (
                    (MatchPoint a, MatchPoint b) =>
                    {
                        if (a.matchValue < b.matchValue)
                            return 1;
                        else if (a.matchValue == b.matchValue)
                            return 0;
                        else
                            return -1;
                    }
                );
            points.Sort(c);
            //get the point with highest match value, then identify if any other points are too close to this point and remove them
            for (int i = 0; i < points.Count; i++)
            {
                if (points[i].Status == 0)
                {
                    for (int j = i + 1; j < points.Count; j++)
                    {
                        int dx = Math.Abs(points[i].TopLeftPoint.X - points[j].TopLeftPoint.X);
                        int dy = Math.Abs(points[i].TopLeftPoint.Y - points[j].TopLeftPoint.Y);
                        if ((dx < bestW / 2) && (dy < bestH / 2))
                        {
                            points[j].Status = 1;
                        }
                    }
                }
            }

            foreach (MatchPoint mp in points)
            {
                if (mp.Status == 0)
                {
                    Rect box = new Rect(mp.TopLeftPoint, new OpenCvSharp.Size(bestW, bestH));
                    BoundingBoxes.Add(box);
                }
            }

            return BoundingBoxes;
        }

        private List<Rect> MatchTemplate(Mat TargetImage, Mat TemplateImage)
        {
            List<Rect> BoundingBoxes = new List<Rect>();

            //Convert image to Mat
            Mat target = new Mat();
            Mat template = new Mat();
            //Convert to gray
            Cv2.CvtColor(TargetImage, target, ColorConversionCodes.BGR2GRAY);
            Cv2.CvtColor(TemplateImage, template, ColorConversionCodes.BGR2GRAY);
            //convert to black and white
            Cv2.Threshold(target, target, 127, 255, ThresholdTypes.BinaryInv);
            Cv2.Threshold(template, template, 127, 255, ThresholdTypes.BinaryInv);
            
            
            Cv2.GaussianBlur(target, target, new OpenCvSharp.Size(config.BlurKernelSize, config.BlurKernelSize), 1);

            //scale the template
            int width = template.Width;
            int height = template.Height;
            int w, h, bestW = width, bestH = height;
            int xSteps = 0;
            int ySteps = 0;
            if (config.xScaleFrom == config.xScaleTo)
            {
                xSteps = 1;
            }
            else
            {
                xSteps = Convert.ToInt32((config.xScaleTo - config.xScaleFrom) / config.xStep);
            }
            if (config.yScaleFrom == config.yScaleTo)
            {
                ySteps = 1;
            }
            else
            {
                ySteps = Convert.ToInt32((config.yScaleTo - config.yScaleFrom) / config.yStep);
            }

            double bestValue = 0;

            progressbar1.Visible = true;
            progressbar1.Minimum = 0;
            progressbar1.Value = 0;

            if (config.Reform)
            {
                progressbar1.Maximum = xSteps * ySteps;
                for (int i = 0; i < xSteps; i++)
                {
                    double xScale = config.xScaleFrom + config.xStep * i;
                    w = Convert.ToInt32(width * xScale);
                    for (int j = 0; j < ySteps; j++)
                    {
                        double yScale = config.yScaleFrom + config.yStep * j;
                        h = Convert.ToInt32(height * yScale);
                        Mat curTemplate = new Mat();
                        Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                        //match scaled template step by step
                        Mat result = new Mat();
                        Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                        double minValue, maxValue;
                        OpenCvSharp.Point minLoc, maxLoc;
                        Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                        if (bestValue < maxValue)
                        {
                            bestValue = maxValue;
                            bestW = w;
                            bestH = h;
                        }
                        if (progressbar1.Value < progressbar1.Maximum)
                            progressbar1.Value++;
                        System.Windows.Forms.Application.DoEvents();
                        result.Dispose();
                        curTemplate.Dispose();
                    }
                }
            }
            else
            {
                progressbar1.Maximum = xSteps;
                for (int i = 0; i < xSteps; i++)
                {
                    double curScale = config.xScaleFrom + config.xStep * i;
                    w = Convert.ToInt32(width * curScale);
                    h = Convert.ToInt32(height * curScale);
                    Mat curTemplate = new Mat();
                    Cv2.Resize(template, curTemplate, new OpenCvSharp.Size(w, h));

                    //match scaled template step by step
                    Mat result = new Mat();
                    Cv2.MatchTemplate(target, curTemplate, result, TemplateMatchModes.CCoeffNormed);
                    double minValue, maxValue;
                    OpenCvSharp.Point minLoc, maxLoc;
                    Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);

                    if (bestValue < maxValue)
                    {
                        bestValue = maxValue;
                        bestW = w;
                        bestH = h;
                    }
                    progressbar1.Value = i + 1;
                    System.Windows.Forms.Application.DoEvents();
                    result.Dispose();
                    curTemplate.Dispose();
                }
            }


            //match again using the best scale
            Mat bestTemplate = new Mat();
            Mat bestResult = new Mat();
            Cv2.Resize(template, bestTemplate, new OpenCvSharp.Size(bestW, bestH));
            Cv2.MatchTemplate(target, bestTemplate, bestResult, TemplateMatchModes.CCoeffNormed);
            //float thresh = 0.6f;
            List<MatchPoint> points = new List<MatchPoint>();
            for (int i = 0; i < bestResult.Rows; i++)
                for (int j = 0; j < bestResult.Cols; j++)
                {
                    float value = bestResult.Get<float>(i, j);
                    if (value >= config.Thresh)
                    {
                        ////注意，rectagnle起始点的x对应Mat的j，y对应Mat的i
                        //Rect box = new Rect(j, i, bestW, bestH);
                        //BoundingBoxes.Add(box);
                        MatchPoint p = new MatchPoint();
                        p.matchValue = value;
                        p.TopLeftPoint = new OpenCvSharp.Point(j, i);
                        p.Status = 0;
                        points.Add(p);
                    }
                }

            bestTemplate.Dispose();
            bestResult.Dispose();

            //remove overlapped bounding boxes
            //sort points by match value
            Comparison<MatchPoint> c = new Comparison<MatchPoint>
                (
                    (MatchPoint a, MatchPoint b) =>
                    {
                        if (a.matchValue < b.matchValue)
                            return 1;
                        else if (a.matchValue == b.matchValue)
                            return 0;
                        else
                            return -1;
                    }
                );
            points.Sort(c);
            //get the point with highest match value, then identify if any other points are too close to this point and remove them
            for (int i = 0; i < points.Count; i++)
            {
                if (points[i].Status == 0)
                {
                    for (int j = i + 1; j < points.Count; j++)
                    {
                        int dx = Math.Abs(points[i].TopLeftPoint.X - points[j].TopLeftPoint.X);
                        int dy = Math.Abs(points[i].TopLeftPoint.Y - points[j].TopLeftPoint.Y);
                        if ((dx < bestW / 2) && (dy < bestH / 2))
                        {
                            points[j].Status = 1;
                        }
                    }
                }
            }

            foreach (MatchPoint mp in points)
            {
                if (mp.Status == 0)
                {
                    Rect box = new Rect(mp.TopLeftPoint, new OpenCvSharp.Size(bestW, bestH));
                    BoundingBoxes.Add(box);
                }
            }

            return BoundingBoxes;
        }

        private void menuMatch_Click(object sender, EventArgs e)
        {
            RunMatch();
        }

        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count == 0)
                return;

            RunMatch();
        }

        private void RunMatch()
        {
            if (listView1.SelectedItems.Count == 0)
            {
                MessageBox.Show("No template selected!");
                return;
            }

            dlgMatchConfig dlg = new dlgMatchConfig(config);
            if(dlg.ShowDialog() == DialogResult.OK)
            {
                this.config = dlg.GetUpdatedConfig();
                foreach (ListViewItem item in listView1.SelectedItems)
                {
                    statusTemplate.Text = item.Text;

                    Mat t = BitmapConverter.ToMat(new Bitmap(Templates[item.ImageIndex].GetImage()));
                    List<Rect> BoundingBoxes = MatchTemplate(ImageToSearch, t);
                    //if (config.ReformType == 0)
                    //{
                    //    BoundingBoxes = MatchTemplate(ImageToSearch, t, config.Thresh);
                    //}
                    //else if (config.ReformType == 1)
                    //{
                    //    BoundingBoxes = MatchTemplateScale(ImageToSearch, t, config.ScaleStart, config.ScaleEnd, config.ScaleStep, config.Thresh);
                    //}
                    //else
                    //{
                    //    BoundingBoxes = MatchTemplateReform(ImageToSearch, t, config.ScaleStart, config.ScaleEnd, config.ScaleStep, config.Thresh);
                    //}
                    //List<Rect> BoundingBoxes = MatchTemplate(ImageToSearch, t, config.ScaleStart, config.ScaleEnd, config.ScaleStep, config.Thresh);
                    
                    foreach (Rect box in BoundingBoxes)
                    {
                        ImageToDisplay.Rectangle(box, Scalar.Green);
                        ImageToDisplay.PutText(item.Text, box.TopLeft, HersheyFonts.HersheyComplex, 0.25, Scalar.Green);
                        ImageToSearch.Rectangle(box, Scalar.White, -1);
                    }
                    statusTemplate.Text = string.Format("{0} {1} found!", BoundingBoxes.Count.ToString(), item.Text);
                }

            }



            imageBox1.Image = ImageToDisplay.ToBitmap();
            //Program.image_show("search", ImageToSearch.ToBitmap());

        }

        private void DetectLines(Mat ImageToDetect)
        {
            Mat imgLine = new Mat();
            Cv2.Threshold(ImageToDetect, imgLine, 127, 255, ThresholdTypes.BinaryInv);
            Cv2.Canny(imgLine, imgLine, 80, 200);
            Program.image_show("Canny", imgLine.ToBitmap());
        }

        private void matchConfigToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (config == null)
            {
                config = new MatchConfig();
            }

            dlgMatchConfig dlg = new dlgMatchConfig(config);
            if(dlg.ShowDialog() == DialogResult.OK)
            {
                config = dlg.GetUpdatedConfig();
            }

            dlg.Dispose();
        }
    }
}
